#include "cc_thread.h"
#include "cc_log.h"
#include "cc_mutex.h"
#include "cc_shell.h"
#include "cc_msg.h"

portMUX_TYPE g_isr_mut = portMUX_INITIALIZER_UNLOCKED;

cc_thread thread_init[CC_CONFIG_THREAD_NUM_MAX];
cc_thread *__thread_init_start = thread_init;
cc_thread *__thread_init_end = thread_init;
#define foreach_thread(pos) \
  for (pos = __thread_init_start; pos < __thread_init_end; pos++)

cc_component_main component_init[CC_CONFIG_COMPONENT_NUM_MAX];
cc_component_main *__component_init_start = component_init;
cc_component_main *__component_init_end = component_init;
#define foreach_component(pos) \
  for (pos = __component_init_start; pos < __component_init_end; pos++)

static cc_tid __cc_thread_auto_create_raw(const char *name, size_t stack_size, int prio,
                                          cc_tid_fun fun, void *data)
{
  cc_tid handle = malloc(sizeof(TaskHandle_t));
  xTaskCreate(fun, name, stack_size, data, prio, &handle);

  return handle;
}

cc_tid cc_thread_create_raw(const char *name, size_t stack_size, int prio,
                            cc_tid_fun fun, void *data)
{
  // 非注册创建的也添加到`管理中
  cc_thread *entry = __thread_init_end;

  entry->fun = fun;
  memcpy(entry->name, name, CC_CONFIG_TID_NAME_MAX);
  entry->prio = prio;
  entry->stack_size = stack_size;
  if (entry->stack_size < CC_CONFIG_THREAD_MIN_STACKSIZE)
  {
    entry->stack_size = CC_CONFIG_THREAD_MIN_STACKSIZE;
  }
  entry->is_create = 1;
  __thread_init_end += 1;

  cc_tid handle = malloc(sizeof(TaskHandle_t));
  xTaskCreate(fun, name, stack_size, data, prio, &handle);
  entry->tid = handle;
  cc_thread_suspend(entry->tid);
  return handle;
}

cc_tid cc_thread_create_stack_size(cc_tid_fun fun, int prio,
                                   size_t stack_size)
{
  return cc_thread_create_raw("default", stack_size, prio, fun, NULL);
}

cc_tid cc_thread_create_default(cc_tid_fun fun, int prio)
{
  return cc_thread_create_stack_size(fun, prio, 2048);
}

void cc_thread_del(cc_tid tid)
{
  vTaskDelete(tid);
}

BaseType_t cc_thread_resume_all(void)
{
  return xTaskResumeAll();
}

void cc_thread_suspend_all(void)
{
  vTaskSuspendAll();
}

void cc_thread_resume(cc_tid tid)
{
  BaseType_t xYieldRequired;
  if (cc_is_in_isr())
  {
    xYieldRequired = xTaskResumeFromISR(tid);

    portYIELD_FROM_ISR(xYieldRequired);
    return;
  }
  else
  {
    vTaskResume(tid);
  }
}

void cc_thread_suspend(cc_tid tid)
{
  vTaskSuspend(tid);
}

int cc_is_in_isr()
{
  return xPortInIsrContext();
}
uint32_t cc_thread_enter_critical(void)
{
  if (cc_is_in_isr())
  {
    return taskENTER_CRITICAL_FROM_ISR();
  }
  else
  {
    taskENTER_CRITICAL(&g_isr_mut);
  }
}

void cc_thread_exit_critical(uint32_t value)
{
  if (cc_is_in_isr())
  {

    taskEXIT_CRITICAL_FROM_ISR(value);
  }
  else
  {
    taskEXIT_CRITICAL(&g_isr_mut);
  }
}

uint32_t cc_thread_get_priority(cc_tid tid)
{
  if (cc_is_in_isr())
  {
    return uxTaskPriorityGetFromISR(tid);
  }
  return uxTaskPriorityGet(tid);
}

void cc_thread_set_priority(cc_tid tid, int prio)
{
  vTaskPrioritySet(tid, prio);
}

char tid_result[3096] = ""; // 初始化结果数组
static void tid_info(char *param, int node)
{
  cc_thread *ptr = __thread_init_start;
  memset(tid_result, 0, sizeof(tid_result));
  char temp[250];
  memcpy(tid_result, "\r\n-----tid info-----\r\n", sizeof("\r\n-----tid info-----\r\n"));
  for (ptr = __thread_init_start; ptr < __thread_init_end; ptr++)
  {
    if (ptr->fun && (strcmp(ptr->name, "") != 0))
    {
      memset(temp, 0, sizeof(temp));
      snprintf(temp, 250, "%s:Stack:%d StackMin:%d Prio:%d State:%d\r\n",
               ptr->name, ptr->stack_size, cc_get_min_stack_using_size(ptr->tid),
               ptr->prio, cc_thread_get_state(ptr->tid));
      strcat(tid_result, temp);
    }
  }
  sprintf(temp, "result lengh:%d\r\n", strlen(tid_result));
  strcat(tid_result, temp);
  strcat(tid_result, "-------------------\r\n");
// 回复
resp:
  cc_msg_resp(node, tid_result, 0, 0);
}

CC_COMMAND_SHELL(tid_info, "list of tid info")

static uint8_t component_cnt = 0;
void cc_component_register(cc_component_fun func, int prio, const char *name)
{
  if (++component_cnt > CC_CONFIG_COMPONENT_NUM_MAX)
  {
    cc_log_e("component:%s register failed\r\n", name);
    cc_log_e("component arr is full can't register please inc the CC_CONFIG_COMPONENT_NUM_MAX in config.h\r\n");
    return;
  }
  cc_component_main *entry = __component_init_end;
  memcpy(entry->name, name, CC_CONFIG_COMPONENT_NAME_MAX);
  entry->fun = func;
  entry->prio = prio;
  __component_init_end += 1;
  return;
}

static uint8_t thread_cnt = 0;
void cc_thread_register(cc_tid_fun func, const char *name, int prio,
                        int stack_size)
{
  if (++thread_cnt > CC_CONFIG_THREAD_NUM_MAX)
  {
    cc_log_e("thread:%s register failed\r\n", name);
    cc_log_e("thread arr is full can't register please inc the CC_CONFIG_COMPONENT_NUM_MAX in config.h\r\n");
    return;
  }
  cc_thread *entry = __thread_init_end;

  entry->fun = func;
  memcpy(entry->name, name, CC_CONFIG_TID_NAME_MAX);
  entry->prio = prio;
  entry->stack_size = stack_size;
  if (entry->stack_size < CC_CONFIG_THREAD_MIN_STACKSIZE)
  {
    entry->stack_size = CC_CONFIG_THREAD_MIN_STACKSIZE;
  }
  entry->is_create = 0;
  __thread_init_end += 1;
  return;
}
uint8_t cc_thread_get_state(cc_tid tid)
{
  return eTaskGetState(tid);
}
uint8_t cc_thread_get_state_by_name(const char *name)
{
  cc_thread *fn_ptr = __thread_init_start;

  foreach_thread(fn_ptr)
  {
    if (strcmp(name, fn_ptr->name) == 0)
    {
      return eTaskGetState(fn_ptr->tid);
    }
  }
  return 0xff;
}
uint8_t cc_thread_get_state_by_cb(cc_tid_fun fun)
{
  cc_thread *fn_ptr = __thread_init_start;

  foreach_thread(fn_ptr)
  {
    if (fn_ptr->fun == fun)
    {
      return eTaskGetState(fn_ptr->tid);
    }
  }
  return 0xff;
}

static void cc_component_init()
{
  cc_component_main *fn_ptr = __component_init_start;
  int i = 8, j = 0;

  for (j = 0; j <= i; j++)
  {
    foreach_component(fn_ptr)
    {
      if (fn_ptr->prio == j && fn_ptr->fun)
      {
        if (fn_ptr->fun() == 1)
          cc_log(":%s success prio%2d\r\n", fn_ptr->name, fn_ptr->prio);
        else
          cc_log(":%s init failed prio%2d\r\n", fn_ptr->name, fn_ptr->prio);
      }
    }
  }
#if (CC_CONFIG_USE_WIFI == 0)
  cc_log("wifi not used now\r\n");
#endif

#if (CC_CONFIG_USR_MQTT == 0 && CC_CONFIG_USE_WIFI == 0)
  cc_log("mqtt not used now\r\n");
#endif

#if (CC_CONFIG_USR_MQTT == 1 && CC_CONFIG_USE_WIFI == 0)
  cc_log_e("cc_config.h error you can't disable wifi when start mqtt\r\n");
#endif

#if (CC_CONFIG_USE_DEVIEMSG == 0)
  cc_log("device msg not used now\r\n");
#endif
}

static void cc_thread_start()
{
  cc_thread *fn_ptr = __thread_init_start;
  foreach_thread(fn_ptr)
  {
    if (fn_ptr->is_create == 0 && fn_ptr->fun && strcmp(fn_ptr->name, "") != 0)
    {
      cc_log("thread %s: stack:%d prio:%d success\r\n", fn_ptr->name,
             fn_ptr->stack_size, fn_ptr->prio);
      fn_ptr->tid = __cc_thread_auto_create_raw(fn_ptr->name, fn_ptr->stack_size,
                                                fn_ptr->prio, fn_ptr->fun, CC_NULL);
      cc_list_node_init(&fn_ptr->node);
      cc_thread_suspend(fn_ptr->tid);
    }
    if (fn_ptr->is_create == 1)
    {
      cc_log("thread %s: stack:%d prio:%d success\r\n", fn_ptr->name,
             fn_ptr->stack_size, fn_ptr->prio);
      cc_list_node_init(&fn_ptr->node);
    }
  }
}

cc_tid cc_get_tid_by_cb(cc_tid_fun fun)
{
  cc_thread *fn_ptr = __thread_init_start;

  foreach_thread(fn_ptr)
  {
    if (fn_ptr->fun == fun)
    {
      return fn_ptr->tid;
    }
  }
  return CC_NULL;
}
cc_tid cc_get_tid_by_name(const char *name)
{
  cc_thread *fn_ptr = __thread_init_start;

  foreach_thread(fn_ptr)
  {
    if (strcmp(name, fn_ptr->name) == 0)
    {
      return fn_ptr->tid;
    }
  }
  return CC_NULL;
}

uint32_t cc_get_min_stack_using_size(cc_tid tid)
{
  if (tid == CC_NULL)
  {
    cc_log_e("tid is null\r\n");
    return 0xFFFFFFFF;
  }
  return uxTaskGetStackHighWaterMark(tid);
}

uint32_t cc_get_min_stack_using_size_by_name(const char *name)
{
  cc_tid tid = cc_get_tid_by_name(name);
  if (tid == CC_NULL)
  {
    cc_log_e("do not have the thread name\r\n");
    return 0xFFFFFFFF;
  }
  return cc_get_min_stack_using_size(tid);
}

uint32_t cc_get_min_stack_using_size_by_cb(cc_tid_fun fun)
{
  cc_tid tid = cc_get_tid_by_cb(fun);
  if (tid == CC_NULL)
  {
    cc_log_e("do not have the fun\r\n");
    return 0xffffffff;
  }
  return cc_get_min_stack_using_size(tid);
}

cc_tid cc_thread_self()
{
  return xTaskGetCurrentTaskHandle();
}

void cc_thread_yield()
{
  taskYIELD();
}

void cc_thread_delete(cc_tid tid)
{
  if (tid == CC_NULL)
  {
    cc_log_e("tid is null\r\n");
    return;
  }
  vTaskDelete(tid);
}

void cc_thread_delete_by_name(const char *name)
{

  cc_tid tid = cc_get_tid_by_name(name);
  if (tid == CC_NULL)
  {
    cc_log_e("do not have the thread name\r\n");
    return;
  }
  cc_thread_delete(tid);
}

void cc_thread_delete_by_cb(cc_tid_fun fun)
{
  cc_tid tid = cc_get_tid_by_cb(fun);
  if (tid == CC_NULL)
  {
    cc_log_e("do not have the fun\r\n");
    return;
  }
  cc_thread_delete(tid);
}

void cc_app_start()
{
  cc_component_init();
  cc_log("all init success\r\n");

  cc_thread_start();
  cc_log("thread start success APP IS ALREADY\r\n");
  cc_thread *fn_ptr = __thread_init_start;
  foreach_thread(fn_ptr)
  {
    if (fn_ptr->fun && (strcmp(fn_ptr->name, "") != 0))
    {
      cc_thread_resume(fn_ptr->tid);
    }
  }
}
